import net from 'net';
import { EventEmitter } from 'events';
import moment from 'moment';
import axios from 'axios';


const CMD_TIMEOUT = 1000; // 命令超时时间（毫秒）
const POLL_INTERVAL = 1000; // 轮询间隔时间（毫秒）
class TcpClient extends EventEmitter {
    constructor(host, port, reconnectInterval = 5000) {
        super();
        this.host = host; // 服务器地址
        this.port = port; // 服务器端口
        this.reconnectInterval = reconnectInterval; // 重连间隔时间（毫秒）
        this.client = null; // TCP 客户端实例
        this.isConnected = false; // 连接状态
        this.reconnectTimer = null; // 重连定时器
        this.baseByteReadStart = [
            0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
            0xB2, 0x00, 0x08, 0x00, 0x4C, 0x02, 0x91, 0x00
        ];
        this.baseByteReadEnd = [0x01, 0x00];
        this.session = new Array(10).fill(0);
        //接收缓存区
        this.receiveData = null;
        //轮询定时器
        this.pollTimer = null; // 定时器
        this.isBusy = false; // 是否忙碌
        this.tagMap = null; // 标签映射表
        /* 值产生了变化的标签 当值发生变化时，更新这个map  读取后清零 */
        this.changeValueMap = new Map(); // 变化值映射表

    }
    async sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    resetPollTimer(callback) {
        if (this.pollTimer) {
            clearInterval(this.pollTimer);
        }
        this.pollTimer = setInterval(() => {
            callback().catch((err) => {
                console.error('Error during polling:', err.message);
            });
        }, POLL_INTERVAL);
    }

    async register() {

        const reg_buf = Buffer.from([
            0x65, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
        ]);
        try {
            this.receiveData = null;
            this.client.write(reg_buf);
            let timeout = moment().add(CMD_TIMEOUT);
            while (moment().isBefore(timeout) && this.receiveData == null) {
                await this.sleep(50);
            }
            if (this.receiveData == null) {
                console.log("注册超时");
                return false;
            }
            if (this.receiveData[0] != 0x65) {
                console.log("注册失败，返回数据头不正确");
                return false;
            }
            let statu = this.receiveData.readUInt32LE(8);
            if (statu != 0x00000000) {
                console.log("注册失败，返回状态值不为0 ");
                return false;
            }
            this.session = this.receiveData.slice(4, 8);
            console.log("注册成功");
            return true;
        } catch (error) {
            console.error('Error connecting:', error);
            this.reconnect();
            return false;
        }
    }
    async readTag(name) {
        try {
            if (!this.client || this.client.destroyed) {
                console.error('Socket is not connected');
                return { code: 0, msg: 'Socket is not connected' };
            }

            const tag = this.getStringByteRead(name);
            this.session.copy(tag, 4);
            this.receiveData = null;
            this.client.write(tag);
            let timeout = moment().add(CMD_TIMEOUT);
            while (moment().isBefore(timeout) && this.receiveData == null) {
                await this.sleep(50);
            }
            if (this.receiveData == null) {
                console.log("读取超时");
                return { code: 0, msg: '读取超时' };
            }
            if (this.receiveData[0] != 0x6F) {
                console.log("读取失败，返回数据头不正确");
                return { code: 0, msg: '读取失败，返回数据头不正确' };
            }
            return this.parseData(this.receiveData);
        } catch (error) {
            console.error(`Error reading tag "${name}":`, error.message);
            return { code: 0, msg: `Error reading tag "${name}": ${error.message}` };
        }
    }

    getStringByteRead(str) {
        try {
            if (str && str.trim() !== '') {
                const array = Buffer.from(str, 'utf-8');
                const k = array.length % 2 !== 0 ? 1 : 0;
                const strLength = array.length;
                const strLengthM = array.length + k + 6;
                const strLengthB = array.length + k + 6 + 16;
                const strLengthO = Math.ceil((array.length + 2) / 2);

                this.baseByteReadStart[2] = strLengthB;
                this.baseByteReadStart[38] = strLengthM;
                this.baseByteReadStart[41] = strLengthO;
                this.baseByteReadStart[43] = strLength;

                const arrayALL = Buffer.alloc(
                    this.baseByteReadStart.length + array.length + k + this.baseByteReadEnd.length
                );

                Buffer.from(this.baseByteReadStart).copy(arrayALL, 0);
                array.copy(arrayALL, this.baseByteReadStart.length);
                Buffer.from(this.baseByteReadEnd).copy(arrayALL, this.baseByteReadStart.length + array.length + k);

                return arrayALL;
            }
            return null;
        } catch (error) {
            console.error('Error in getStringByteRead:', error);
            return null;
        }
    }

    parseData(buf) {
        if (buf[42] === 0x00) {
            const length = buf.readUInt16LE(2);
            const msg304 = buf.subarray(46, 46 + length - 22);
            const type = buf[44];
            switch (type) {
                case 0xC3: // int
                    return { code: 1, val: msg304.readInt16LE(0) };
                case 0xCA: // float
                    return { code: 1, val: msg304.readFloatLE(0) };
                case 0xC1: // bool
                    return { code: 1, val: !!msg304[0] }
                case 0xD0: // string
                    const lenStr = buf.readUInt16LE(46);
                    return { code: 1, val: buf.slice(48, 48 + lenStr).toString('utf-8') };
                case 0xC4: // DINT
                    return { code: 1, val: msg304.readUInt32LE(0) };
                default:
                    console.error('Unsupported data type:', type);
                    return { code: 0, msg: 'Unsupported data type' };
            }
        }
        else {
            switch (buf[42]) {
                case 0x01:
                    console.error('Error: 无效命令');
                    return { code: 0, msg: '无效命令' };
                case 0x02:
                    console.error('Error: 无效数据');
                    return { code: 0, msg: '无效数据' };
                case 0x03:
                    console.error('Error: 数据格式错误，或者传入的参数不合法');
                    return { code: 0, msg: '数据格式错误，或者传入的参数不合法' };
                case 0x04:
                    console.error('Error: 所请求的资源（如设备、IO点、数据等）不可用。');
                    return { code: 0, msg: '所请求的资源（如设备、IO点、数据等）不可用。' };
                case 0x05:
                    console.error('Error: 无效路径');
                    return { code: 0, msg: '无效路径' };
                case 0x06:
                    console.error('Error: 资源被锁定');
                    return { code: 0, msg: '资源被锁定' };
                case 0x07:
                    console.error('Error: 超时');
                    return { code: 0, msg: '超时' };
                case 0x08:
                    console.error('Error: 无效属性');
                    return { code: 0, msg: '无效属性' };
                case 0x09:
                    console.error('Error: 服务不支持');
                    return { code: 0, msg: '服务不支持' };
                case 0x0A:
                    console.error('Error: 无效对象');
                    return { code: 0, msg: '无效对象' };
                case 0x0B:
                    console.error('Error: 资源不足');
                    return { code: 0, msg: '资源不足' };
                case 0x0C:
                    console.error('Error: 无效长度');
                    return { code: 0, msg: '无效长度' };
                case 0x0D:
                    console.error('Error: 一般错误');
                    return { code: 0, msg: '一般错误' };
                default:
                    console.error('Error: 未知错误代码', buf[42]);
                    return { code: 0, msg: '未知错误代码' };
            }

        }


    }

    // 连接到服务器
    connect() {
        return new Promise((resolve, reject) => {
            this.client = new net.Socket();

            // 连接到服务器
            this.client.connect(this.port, this.host, () => {
                this.isConnected = true;
                console.log(`Connected to ${this.host}:${this.port}`);
                let res = this.register();
                if (!res) {
                    console.error('注册失败');
                    this.disconnect(); // 断开连接
                    this.isConnected = false;
                    this.reconnect(); // 尝试重连
                    return reject(new Error('注册失败'));
                }
                // 启动轮询读取标签值
                this.resetPollTimer(async () => {
                    await this.pollReadTag(); // 确保异步方法被正确调用
                });

                resolve();
            });

            // 监听数据接收
            this.client.on('data', (data) => {
                console.log('Received:', data.toString('hex'));
                this.receiveData = data; // 保存接收到的数据
            });

            // 监听连接关闭
            this.client.on('close', () => {
                console.log('Connection closed');
                this.isConnected = false;
                this.reconnect(); // 尝试重连
            });

            // 监听错误
            this.client.on('error', (err) => {
                console.error('Connection error:', err.message);
                this.isConnected = false;
                this.reconnect(); // 尝试重连
                reject(err);
            });
        });
    }

    // 轮询读取标签值
    async pollReadTag() {
        if (this.isBusy) return;

        clearInterval(this.pollTimer); // 清除定时器
        this.isBusy = true;
        // this.changeValueMap.clear(); // 清空变化值映射表
        try {
            //遍历标签映射表，读取每个标签的值
            for (let [tag, value] of this.tagMap) {
                let res = await this.readTag(tag); // 读取标签
                console.log("读取标签", tag, "值", res);
                if (res.code == 1) {
                    if (value.value != res.val)//如果值发生变化
                    {
                        let newValue = value;
                        newValue.value = res.val;
                        this.tagMap.set(tag, newValue); // 更新标签映射表
                        this.changeValueMap.set(tag, newValue); // 更新变化值映射表
                    }
                } else {
                    console.error(`Failed to read tag ${tag}:`, tagValue.msg);
                }
            }
            console.log("轮询读取结束");

        } catch (error) {
            console.error('轮询读取出现未预期错误:', error.message);
        } finally {
            this.emit('tagReadOver', this.changeValueMap); // 触发事件，传递标签映射表
            //全寄存器读取一次
            this.isBusy = false;
            // 启动轮询读取标签值
            this.resetPollTimer(async () => {
                await this.pollReadTag(); // 确保异步方法被正确调用
            });
        }


    }
    // 发送数据到服务器
    send(data) {

        if (this.isConnected) {
            try {
                // 发送数据
                this.client.write(data);
                console.log('Sent:', data.toString('hex'));
                return true;
            }
            catch (error) {
                console.error('Error sending data:', error.message);
                this.disconnect(); // 断开连接
                this.reconnect(); // 尝试重连
            }
        } else {
            console.error('Cannot send data, client is not connected');
        }
        return false;
    }

    // 关闭连接
    disconnect() {
        if (this.client) {
            try {
                this.client.end();
                this.client.destroy();
                this.isConnected = false;
                console.log('Disconnected from server');
            }
            catch (error) {
                console.error('尝试关闭连接出错', error.message);
            }

        }
        if (this.pollTimer) {
            clearInterval(this.pollTimer); // 清除定时器
            this.pollTimer = null;
            console.log('清除轮询定时器');
        }
    }

    // 重连逻辑
    reconnect() {
        if (this.reconnectTimer) return; // 如果已经在重连中，则不重复设置

        console.log(`Attempting to reconnect in ${this.reconnectInterval / 1000} seconds...`);
        this.reconnectTimer = setTimeout(() => {
            this.reconnectTimer = null; // 清除重连定时器
            this.connect().catch((err) => {
                console.error('Reconnect failed:', err.message);
            });
        }, this.reconnectInterval);
    }

}

// // 示例用法


// let tagMap = new Map();
// tagMap.set('A14.设备故障中', { value: 0, id: 1 });
// tagMap.set('A14.正常生产中', { value: 0, id: 2 });
// tagMap.set('A14.生产待料中', { value: 0, id: 3 });
// tagMap.set('A14.设备状态', { value: 0, id: 4 });
// tagMap.set('A14.可动率', { value: 0, id: 5 });
// tagMap.set('A14.稼动率', { value: 0, id: 6 });
// tagMap.set('A14.生产总秒', { value: 0, id: 7 });
// tagMap.set('A14.车型代码', { value: '', id: 8 });
// client.tagMap = tagMap; // 设置标签映射表

// const BASE_URL = '118.31.106.200:83'
const BASE_URL = '127.0.0.1:83'
async function test() {

    let res = await getPlcInfo();
    console.log('获取到的plc信息:', res);
    for (let i = 0; i < res.length; i++) {
        let plc = res[i];
        let tagMap = await getTags(plc.id); // 获取标签映射表
        console.log('获取到的标签映射表:', tagMap);
        const client = new TcpClient(plc.ip, 44818);
        client.tagMap = tagMap; // 设置标签映射表
        client.on('tagReadOver', async (data) => {

            let changeArray = Array.from(data.entries()).map(([key, value]) => ({ key, value }));
            let str = JSON.stringify(changeArray);
            if (changeArray.length > 0) {
                console.log('变化值:', str); // 输出变化值
                try {
                    let url = `http://${BASE_URL}/api/realData/report`;
                    let res = await axios.post(url, changeArray);
                    console.log('上报结果:', res.data); // 输出变化值
                } catch (error) {
                    console.error('Error sending data:', error.message);
                }
                data.clear()
            }
        });
        try {
            await client.connect(); // 连接到服务器
            console.log("连接成功");
        }
        catch (error) {
            console.error('连接失败:', error.message);
        }
    }




}
test();
//从服务器获取标签值
async function getTags(plc_id) {
    let url = `http://${BASE_URL}/api/getregister`;
    console.log('plc_id', plc_id);
    try {
        let res = await axios.post(url, { plc_id });
        console.log('getTags.res', res.data);
        if (res.data.code == 0) {
            let tags = res.data.data;
            let tagMap = new Map();
            for (let i = 0; i < tags.length; i++) {
                let tag = tags[i];
                if (tagMap.register_type == 1) {
                    tagMap.set(tag.register_tag, { value: tag.str_value, id: tag.id });
                } else {
                    tagMap.set(tag.register_tag, { value: tag.last_value, id: tag.id });
                }

            }
            return tagMap;
        }


    } catch (error) {
        console.error('Failed to read tag:', error.message);
    }
    return null;
}
//从服务器获取PLC的信息
async function getPlcInfo() {
    let url = `http://${BASE_URL}/api/getPlcList`;
    try {
        let res = await axios.post(url);
        console.log('getPlcInfo.res', res.data);
        if (res.data.code == 0) {
            let plcInfo = res.data.data;
            return plcInfo;
        }
    }
    catch (error) {
        console.error('getPlcInfo error:', error.message);
    }
}


// client.connect()
//     .then(() => {
//         client.send('Hello, server!');
//     })
//     .catch((err) => {
//         console.error('Failed to connect:', err.message);
//     });

// // 监听进程退出时关闭连接
// process.on('SIGINT', () => {
//     client.disconnect();
//     process.exit();
// });